/**
 * \file Pic.h
 * \brief PCI facilities
 * \author Corey Tabaka
 */

/*
   Copyright 2006 Corey Tabaka

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include <ktypes.h>

#ifndef __KERNEL_PCI_H__
#define __KERNEL_PCI_H__

/*
 * PCI access return codes
 */
#define _PCI_SUCCESSFUL				0x00
#define _PCI_FUNC_NOT_SUPPORTED		0x81
#define _PCI_BAD_VENDOR_ID			0x83
#define _PCI_DEVICE_NOT_FOUND		0x86
#define _PCI_BAD_REGISTER_NUMBER	0x87
#define _PCI_SET_FAILED				0x88
#define _PCI_BUFFER_TOO_SMALL		0x89

/*
 * PCI configuration space offsets
 */
#define PCI_CONFIG_VENDOR_ID		0x00
#define PCI_CONFIG_DEVICE_ID		0x02
#define PCI_CONFIG_COMMAND			0x04
#define PCI_CONFIG_STATUS			0x06
#define PCI_CONFIG_REVISION_ID		0x08
#define PCI_CONFIG_CLASS_CODE		0x09
#define PCI_CONFIG_CACHE_LINE_SIZE	0x0c
#define PCI_CONFIG_LATENCY_TIMER	0x0d
#define PCI_CONFIG_HEADER_TYPE		0x0e
#define PCI_CONFIG_BIST				0x0f
#define PCI_CONFIG_BASE_ADDRESSES	0x10
#define PCI_CONFIG_CARDBUS_CIS_PTR	0x28
#define PCI_CONFIG_SUBSYS_VENDOR_ID	0x2c
#define PCI_CONFIG_SUBSYS_ID		0x2e
#define PCI_CONFIG_EXP_ROM_ADDRESS	0x30
#define PCI_CONFIG_CAPABILITIES		0x34
#define PCI_CONFIG_INTERRUPT_LINE	0x3c
#define PCI_CONFIG_INTERRUPT_PIN	0x3d
#define PCI_CONFIG_MIN_GRANT		0x3e
#define PCI_CONFIG_MAX_LATENCY		0x3f

/*
 * PCI command register bits
 */
#define PCI_COMMAND_IO_EN			0x0001
#define PCI_COMMAND_MEM_EN			0x0002
#define PCI_COMMAND_BUS_MASTER_EN	0x0004
#define PCI_COMMAND_SPECIAL_EN		0x0008
#define PCI_COMMAND_MEM_WR_INV_EN	0x0010
#define PCI_COMMAND_PAL_SNOOP_EN	0x0020
#define PCI_COMMAND_PERR_RESP_EN	0x0040
#define PCI_COMMAND_AD_STEP_EN		0x0080
#define PCI_COMMAND_SERR_EN			0x0100
#define PCI_COMMAND_FAST_B2B_EN		0x0200

/*
 * PCI status register bits
 */
#define PCI_STATUS_NEW_CAPS			0x0010
#define PCI_STATUS_66_MHZ			0x0020
#define PCI_STATUS_FAST_B2B			0x0080
#define PCI_STATUS_MSTR_PERR		0x0100
#define PCI_STATUS_DEVSEL_MASK		0x0600
#define PCI_STATUS_TARG_ABORT_SIG	0x0800
#define PCI_STATUS_TARG_ABORT_RCV	0x1000
#define PCI_STATUS_MSTR_ABORT_RCV	0x2000
#define PCI_STATUS_SERR_SIG			0x4000
#define PCI_STATUS_PERR				0x8000

typedef struct {
	uint16 vendor_id;
	uint16 device_id;
	uint16 command;
	uint16 status;
	union {
		struct {
			uint32 revision_id : 8;
			uint32 class_code : 24;
		};
		struct {
			uint8 revision_id_0;
			uint8 program_interface;
			uint8 sub_class;
			uint8 base_class;
		};
	};
	uint8 cache_line_size;
	uint8 latency_timer;
	uint8 header_type;
	uint8 bist;
	uint32 base_addresses[6];
	uint32 cardbus_cis_ptr;
	uint16 subsystem_vendor_id;
	uint16 subsystem_id;
	uint32 expansion_rom_address;
	struct {
		uint32 capabilities_ptr : 8;
		uint32 reserved_0 : 24;
	};
	uint32 reserved_1;
	uint8 interrupt_line;
	uint8 interrupt_pin;
	uint8 min_grant;
	uint8 max_latency;
} __attribute__((packed)) pci_config_t;

/**
 * \brief PCI address structure.
 */
typedef struct
{
	uint8 bus;
	union {
		uint8 dev_fn;
		struct {
			uint8 function : 3;
			uint8 device : 5;
		};
	};
} pci_location_t;

typedef struct {
	uint8 id;
	uint8 next;
} __attribute__((packed)) pci_capability_t;

typedef struct {
	uint8 bus;
	uint8 device;
	uint8 linkIntA;
	uint16 irqIntA;
	uint8 linkIntB;
	uint16 irqIntB;
	uint8 linkIntC;
	uint16 irqIntC;
	uint8 linkIntD;
	uint16 irqIntD;
	uint8 slot;
	uint8 reserved;
} __attribute__ ((packed)) irq_routing_entry;

class Pci {
	protected:
	
	Pci(void);
	~Pci(void);
	
	public:
	
	static void initialize(void);
	
	static int findDevice(pci_location_t &state, uint16 device_id,
		uint16 vendor_id, uint16 index);
	static int findClassCode(pci_location_t &state, uint32 class_code,
		uint16 index);
	
	static int readConfig(pci_location_t &state, uint32 reg, uint8 *value);
	static int readConfig16(pci_location_t &state, uint32 reg, uint16 *value);
	static int readConfig32(pci_location_t &state, uint32 reg, uint32 *value);
	static int writeConfig(pci_location_t &state, uint32 reg, uint8 value);
	static int writeConfig16(pci_location_t &state, uint32 reg, uint16 value);
	static int writeConfig32(pci_location_t &state, uint32 reg, uint32 value);
	
	static int getIrqRoutingOptions(irq_routing_entry *entries, uint16 &count,
		uint16 &pciIrqs);
	static int setIrqHwInt(pci_location_t &state, uint8 int_pin, uint8 irq);
};

#endif
